package hello.repository;


import hello.dto.Customer;
import io.r2dbc.spi.Row;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.r2dbc.core.RowsFetchSpec;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author luo
 * @since 2022/9/15 17:12
 * 参考： https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#r2dbc
 */
@Component
public class DatabaseClientRepository {


    private final DatabaseClient databaseClient;

    private final Function<Row, Customer> rowCustomerFunction = (Row row) -> {
        /**
         * 关系型数据库可以返回空值
         * 但是响应式框架不接受空值，所以此函数必须返回一个非null对象
         * 可以考虑使用 Optional 对象来包装
         * */
        return new Customer(
//              大小写不敏感
                row.get("id", Long.class),
                row.get("first_namE", String.class),
                row.get("lasT_name", String.class)
        );
    };

    public DatabaseClientRepository(DatabaseClient databaseClient) {
        this.databaseClient = databaseClient;
    }


    public Flux<Customer> pageCustomers(int index, int offset) {

        return databaseClient.sql(String.format("select * from customer limit %d,%d", index, offset))
                .map(rowCustomerFunction).all();
    }


    public Flux<Customer> detail(Long id) {
//        测试参数绑定
//        r2dbc 支持将 :id 标记 转换为 数据库原生的绑定标记
//        举例 :id 会被转换为 sql server 的 @id 参数标记
//             :id 会被 转换为 postgres 的 $1 $2 $3 等 参数标记
        return databaseClient.sql("select * from customer where id = :id")
                .bind("id", id)
                .map(rowCustomerFunction)
                .all();
    }

    public Flux<Customer> byFirstLastName(List<Customer> customers) {
//        元组 参数集合
        List<Object[]> tuples = customers.stream().map(cus -> {
            return new Object[]{
                    cus.getFirstName(),
                    cus.getLastName()
            };
        }).collect(Collectors.toList());

//        使用元组来传递 动态长度 参数的 参数 ，
//        然后 query-preprocessor  来拼接 动态参数长度的 sql
        return databaseClient.sql("select * from customer where (first_name  , last_name ) in ( :tuples)")
                .bind("tuples", tuples)
                .map(rowCustomerFunction)
                .all();
    }

    /**
     * 插入记录的同时，获取插入行的id
     *
     * @param customer
     * @return
     */
    public Mono<Integer> insertAndGetId(Customer customer) {
        return databaseClient.sql("insert into customer(first_name  , last_name) value(:firstName  , :lastName)")
                .filter(statement -> {
//                  通过注册 statementFilter 可以在 sql 声明运行之前，对其进行一些微调
//                    如果需要更复杂的功能，可以考虑实现 org.springframework.r2dbc.core.StatementFilterFunction 接口
//                    并将其注册的过滤器中
                    return statement.returnGeneratedValues("id");
                })
                .bind("firstName", customer.getFirstName())
                .bind("lastName", customer.getLastName())
                .map((Row row) -> {
                    return row.get("id", Integer.class);
                })
                .first();
    }
}
